package exchange.core;

import java.io.ObjectOutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.LinkedList;
import java.util.logging.Logger;

import exchange.util.*;
/**
 * 
 * @author Israelit Eugenii
 *
 *Exchange protocol:
 *
 *LOGIN;<name>;
 *LOGOUT;
 *
 *ORDER;<id>;<OrderType: BID/ASK>,<StockName>,<Price>,<Amount>;
 *CANCEL;<id>;<OrderType: BID/ASK>,<StockName>;
 *
 *GETSTOCKS; - returns an available stocks on exchange;
 *GETSTOCKGLASS;<stockNmae>; - returns a glass data;
 *
 */

public class Parser {
	private static Logger log = Logger.getLogger(Parser.class.getName());
	private ObjectOutputStream outputStream;
	Client client = null;

	public Parser(ObjectOutputStream out){
		this.outputStream = out;
	}
	
	public void execute(String str) throws ParseException{

		String[] s = str.split(";");
		String command = s[0];
		
		if(client == null && !command.equals("LOGIN")){
			throw new ParseException("LOGINERROR");
		}
		
		switch (command) {
		case "LOGIN":
			login(s[1]);
			break;
			
		case "ORDER":	
			Order newOrder = getOrder(s);
			Mail.send(outputStream, "Order " + s[1] + " accepted");
			Matcher.match(newOrder);
			break;
			
		case "CANCEL":
			Mail.send(outputStream, "Cancel " + s[1] + " accepted");
			Mail.send(outputStream, 
					Cancel.cancelOrder(client, getOrderId(s[1]), getList(getStock(s[3]), s[2]) ) );
			break;
		case "GETSTOCKS":
			Mail.send(outputStream, 
					InfoService.sendStocks() );
			break;
			
		case "GETSTOCKGLASS":
			Mail.send(outputStream, 
					InfoService.sendStockGlass( getStock( s[1] ) ) );
			break;
			
		case "LOGOUT":
			log.info("LOGOUT " + client.getName());
			break;
			
		default:
			throw new ParseException("unknown command " + command );
		}
	}

	private void login(String name){
		this.client = new Client(name, outputStream);
		log.info("LOGIN: " + client.getName());
		Mail.send(outputStream, "LOGINSUCCESS");
	}

	private Order getOrder(String[] s) throws ParseException {

		if (s.length != 6){
			throw new ParseException("there must be 6 parameters in order: " + s);
		}
		
		int orderId = getOrderId(s[1]);

		OrderType orderType;
		if (s[2].equals("ASK") ) {
			orderType = OrderType.ASK;
		} else if (s[2].equals("BID") ) {
			orderType = OrderType.BID;
		} else {
			throw new ParseException("Incorrect orderType - " + s[0] );
		}
		
		Stock stock = getStock(s[3]);

		float price;
		try {
			price = Float.parseFloat(s[4]);
			if (price <= 0){
				throw new ParseException("price sholud be > 0: " + s[4]);
			}
			price = new BigDecimal(price).setScale(3, RoundingMode.HALF_DOWN).floatValue();

		} catch(NumberFormatException numExc) {
			throw new ParseException("Wrong price " + s[4] + numExc.getMessage());
		}
		
		int amount;
		try {
			amount = Integer.parseInt(s[5]);
			if (amount <= 0){
				throw new ParseException("incorrect amount: " + s[5]);
			}
		} catch(NumberFormatException numExc) {
			throw new ParseException("incorrect amount " + s[5] + numExc.getMessage());
		}

		return new Order(this.client, orderId, stock, orderType, price, amount);
	}
	
	private int getOrderId(String str) throws ParseException{
		try {
			return Integer.parseInt(str);
		} catch(NumberFormatException numExc) {
			throw new ParseException("wrong ID " + str + numExc.getMessage());
		}
	}

	private Stock getStock(String str) throws ParseException {
		for (Stock stock :Exchange.getStocks()){
			if (stock.getName().equals(str) ) {
				return stock;
			}
		}
		throw new ParseException("unknown Stock " + str);
	}

	private LinkedList<Order> getList(Stock stock, String orderType) {
		LinkedList<Order> list = orderType.equals("BID") ? stock.getBidList() : stock.getAskList();
		return list;
	}

	
}
